/*
   FieldVis - A Tool for Visualizing Astrophysical Magnetohydrodynamic Flow
   
   Copyright (C) 2006 - 2008     FieldVis Development Team
  
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
  
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
  
   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

   File        : ResourceFile.cpp
   Author      : Jeremy Moc  (mocj@msi.umn.edu)

   Description : Functions to initialize the directory resources for
                 default file locations.
*/

#include <unistd.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <memory>

/*
   This function validates the supplied path.  Given a path it returns the
   absolute path.
   
   TO DO: needs work yet....
*/
std::string GetAbsolutePath(const std::string &dir)
{
   std::string absolute(dir);
   
   // first char in arg is not a '/'
   if ( dir.find('/', 0) != 0 )
   {
      // first char in arg is '~'
      if ( dir.find('~', 0) == 0 )
      {
         std::string HOME = getenv("HOME");
         absolute = HOME + absolute.substr(1);  // remove the '~'
      }
      else
      {
         // TO DO: how will we handle relative paths?
         //  For now - do not allow relative paths - show error message & quit.
         std::cerr << "ERROR: Relative path in resource not allowed.\n"
                   << "       Path must be absolute: "
                   << absolute << std::endl;
         exit (-1);
      }
      
      // TO DO: may want to check the path for more wierdness....
   }
   
   // TO DO: may want to check the path for more wierdness....

   return (absolute);
}

/*
   Add the supplied path to the resource container if it's not already.
*/
void AddPathToResource(const std::string &str, std::vector<std::string> &rc)
{
   std::string path = GetAbsolutePath(str);

   if (find(rc.begin(), rc.end(), path) == rc.end())
   {
      rc.push_back(path);
   }
}

/*
   Split the input string into its component tokens and put each into
   the resource container.  The expected format is a colon ':' delimited
   string of absolute paths.  e.g.:  "/path/one:/path/two:/path/three"
*/
void ParseColonDelimitedString(const std::string &str, std::vector<std::string> &rc)
{
   std::string substring;
   std::string::size_type  index = 0, findex;

   while (index < str.length())
   {
      findex = str.find(':', index);
      if (findex == std::string::npos)    // need this to account for 
      {                                   // no trailing ':' in string
         findex = str.length();
      }
      substring = str.substr(index, findex - index);
      AddPathToResource(substring, rc);
      index = findex + 1;  // skip next ':'
   }
}

/*
   Initialise the resource container from either or both of:
      1. FIELDVISRC environment variable
      2. ~/.fieldvisrc file
*/
void ParseExternalRCFile(std::vector<std::string> &rc)
{
   std::string input;
   
   // if the FIELDVISRC environment variable exists parse it out.
   
   if (getenv("FIELDVISRC") != NULL)
   {
      input = getenv("FIELDVISRC");
      ParseColonDelimitedString(input, rc);
   }

   // if the ~/.fieldvisrc file exists parse it out.
   
   input = getenv("HOME");
   input += "/.fieldvisrc";
   
   std::ifstream rc_file(input.c_str(), std::ios::in);
   
   if (rc_file)
   {
      getline(rc_file, input);
      ParseColonDelimitedString(input, rc);
   }
}

/*
   Given a filename with or without any path components - generate the
      absolute path to the file.
*/
std::string GetFullPathToFile(const std::string &filename)
{
   std::string    fullPath(filename);

   // first char in arg is not a '/'
   if ( filename.find('/', 0) != 0 )
   {
      // first char in arg is '~'
      if ( filename.find('~', 0) == 0 )
      {
         std::string HOME = getenv("HOME");
         fullPath = HOME + fullPath.substr(1);  // remove the '~'
      }
      else 
      {
         // need to get rid or any . or .. in the absolute path.  easiest way
         // to do this is by cd'ing to the dir and then getcwd() then restoring.
         
         char cwd[PATH_MAX];
         getcwd(cwd, PATH_MAX);

         // no char in arg is '/', ie. filename arg has no path component to it
         if ( filename.find('/', 0) == std::string::npos )
         {
            fullPath = std::string(cwd) + "/" + filename;
         }
         else
         {
            char working[PATH_MAX], fname[FILENAME_MAX];
   
            sprintf(working, "%s/%s", cwd, filename.c_str());
            char *p = strrchr(working, '/');
            *p = 0;
            strcpy(fname, p + 1);
            chdir(working);
            getcwd(working, PATH_MAX);
            chdir(cwd);
            fullPath = std::string(working) + "/" + fname;
         }
      }
   }
   
   return ( fullPath );
}

/*
   Given a filename, current working directory and a resource list, find the
   search the cwd and then the rc list for the first file that exists with
   the given name and return the entire path.
*/
extern std::string GetFullPathToFileCheckRC(const std::string &filename,
                                            const std::string &cwd,
                                            const std::vector<std::string> &rc)
{
   std::string fullpath;
   std::ifstream rc_file;
   
   fullpath = cwd + "/" + filename;
   rc_file.open(fullpath.c_str(), std::ios::in);

   if ( ! rc_file)
   {
      std::vector<std::string>::const_iterator  iter = rc.begin();
   
      for ( ; iter != rc.end(); ++iter )
      {
         fullpath = *iter + "/" + filename;
         rc_file.open(fullpath.c_str(), std::ios::in);
   
         if (rc_file)
         {
            break;   // if the file exists we break out
         }
      }
   }

   if ( ! rc_file)
   {
      std::cerr << "Could not find file: " << filename << " in any search path." << std::endl;
      exit(1);   
   }

   rc_file.close();
   
   return (fullpath);
}

